/*
 *  Copyright 2008 Mark Ashworth <javameme@gmail.com>.
 *
 *  Licensed under the GNU General Public License v3 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.gnu.org/licenses/gpl-3.0.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package xperiment.metaphor.model.entity;

import xperiment.metaphor.model.contact.Address;
import xperiment.metaphor.model.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.persistence.*;

import metaphor.service.ServiceException;

/**
 * Company
 *
 * @author Mark Ashworth
 * @version 1.0.0
 * @since 1.0.0
 */
//@Entity
//@Table(name="company")
public class Company extends DomainObject implements Persistent, Named {

    /** An empty company object */
    public static final Company EMPTY = new Company();

    /* The unique database id for the object */
    //@Id @GeneratedValue(strategy=GenerationType.AUTO)
    //@Column(name = "company_id", unique = true, nullable = false)
    private Long id = null;
    
    /* The name of the company */
    //@Column(name="company_name", unique=true, nullable=false)
    private String name;

    /** The entity information of the company. */
    //@OneToOne
    //@JoinColumn(name="entity")
    private xperiment.metaphor.model.entity.Entity entity = new xperiment.metaphor.model.entity.Entity();

    /* Whether the object is enabled */
    //@Column(name = "enabled", nullable = false)
    private boolean enabled = true;
    
    /** The list of financial periods of a company */
    private Set<FinancialPeriod> financialPeriods = new HashSet<FinancialPeriod>();
    
    /** The list of clients of the company */
    //@Transient
    private Set<Client> clients = new HashSet<Client>();
    
    /** The list of departments in the company */
    private SortedSet<Department> departments = new TreeSet<Department>();
    
    /**
     * Default constructor
     */
    public Company() {
        super();
    }
    
    /**
     * Creates the company with the name
     * @param name The company's name
     */
    public Company(String name) {
        super();
        setName(name);
    }

    /**
     * Creates the company with the name and address
     * @param name The company's name
     * @param postalAdress The company's postal address
     */
    public Company(String name, Address postalAdress) {
        super();
        setName(name);
        getEntity().getContactInformation().setPostalAddress(postalAdress);
    }
    
    /**
     * Creates the company with the name and address
     * @param name The company's name
     * @param postalAddress The company's postal address
     * @param physicalAddress The company's physical address
     */
    public Company(String name, Address postalAddress, Address physicalAddress) {
        super();
        setName(name);
        getEntity().getContactInformation().setPostalAddress(postalAddress);
        getEntity().getContactInformation().setPhysicalAddress(physicalAddress);
    }
    
    /**
     * Creates the company
     * @param name The company's name
     * @param postalAddress The company's postal address
     * @param physicalAddress The company's physical address
     * @param departments The company's departments
     */
    public Company(String name, Address postalAddress, Address physicalAddress, SortedSet<Department> departments) {
        super();
        setName(name);
        getEntity().getContactInformation().setPostalAddress(postalAddress);
        getEntity().getContactInformation().setPhysicalAddress(physicalAddress);
        setDepartments(departments);
    }
    
    /**
     * Creates the company
     * @param name The company's name
     * @param postalAddress The company's postal address
     * @param physicalAddress The company's physical address
     * @param departments The company's departments
     * @param financialPeriods The company's financial periods
     */
    public Company(String name, Address postalAddress, Address physicalAddress, SortedSet<Department> departments, List<FinancialPeriod> financialPeriods) {
        super();
        setName(name);
        getEntity().getContactInformation().setPostalAddress(postalAddress);
        getEntity().getContactInformation().setPhysicalAddress(physicalAddress);
        setDepartments(departments);
        setFinancialPeriods(financialPeriods);
    }
    
    /**
     * The unique database id for the object
     * @return Long
     */
    @Override
    public Long getId() {
        return this.id;
    }
    
    /**
     * The name of the company
     * @return String
     */
    @Override
    public String getName() {
        return this.name;
    }

    /**
     * The name of the company
     * @param name The new value
     */
    public void setName(String name) {
        String old = getName();
        this.name = name;
        firePropertyChange("name", old, getName());
    }

    /**
     * Returns the entity information of this organization
     * @return Entity
     */
    protected xperiment.metaphor.model.entity.Entity getEntity() {
        if (this.entity == null) {
            this.entity = new xperiment.metaphor.model.entity.Entity();
        }
        return this.entity;
    }

    /**
     * Gets the organizations's telephone number
     * @return String
     */
    public String getTelephoneNo() {
        return this.getEntity().getContactInformation().getTelephoneNo();
    }

    /**
     * Gets the organizations's cellphone number
     * @return String
     */
    public String getCellphoneNo() {
        return this.getEntity().getContactInformation().getCellphoneNo();
    }

    /**
     * Gets the organizations's facsimile number
     * @return String
     */
    public String getFacsimile() {
        return this.getEntity().getContactInformation().getFacsimileNo();
    }

    /**
     * Gets the organizations's email
     * @return String
     */
    public String getEmail() {
        return this.getEntity().getContactInformation().getEmail();
    }

    /**
     * Gets the organizations's postal address
     * @return Address
     */
    public Address getPostalAddress() {
        return this.getEntity().getContactInformation().getPostalAddress();
    }

    /**
     * Gets the organizations's physical address
     * @return Address
     */
    public Address getPhysicalAddress() {
        return this.getEntity().getContactInformation().getPhysicalAddress();
    }

    /**
     * Departments
     * @return Unmodifiable list of departments
     */
    public SortedSet<Department> getDepartments() {
        if (this.departments == null) {
            this.departments = new TreeSet<Department>();
        }
        return this.departments;
    }
    
    /**
     * Sets the list of departments
     * @param departments The new departments
     */
    public void setDepartments(SortedSet<Department> departments) {
//        if (this.departments == null) {
//            this.departments = new TreeSet<Department>();
//        } else {
//            this.departments.clear();
//        }
//        this.departments.addAll(departments);
        this.departments = departments;
    }

    /**
     * Removes a department to the list of departments
     * @param department The department to remove
     * @return Whether the department was removed
     */
    public boolean removeDepartment(Department department) {
        if (this.departments == null) {
            this.departments = new TreeSet<Department>();
        }
        return this.departments.remove(department);
    }
    
    /**
     * Adds a list of departments to the list of departments
     * @param departments The departments to add
     * @return Whether the departments were added
     */
    public boolean addDepartments(Collection<Department> departments) {
        if (this.departments == null) {
            this.departments = new TreeSet<Department>();
        }
        return this.departments.addAll(departments);
    }
    
    /**
     * Removes a list of departments from the list of departments
     * @param departments The departments to remove
     * @return Whether the departments were removed
     */
    public boolean removeDepartments(List<Department> departments) {
        if (this.departments == null) {
            this.departments = new TreeSet<Department>();
        }
        return this.departments.removeAll(departments);
    }
    
    /**
     * The clients of the company
     * @return Unmodifiable List of clients
     */
    public List<Client> getClients() {
        if (this.clients == null) {
            this.clients = new HashSet<Client>();
        }
        return Collections.unmodifiableList(new ArrayList(this.clients));
    }
    
    /**
     * The clients of the company
     * @param clients The new list of clients
     */
    public void setClients(Collection<Client> clients) {
        if (this.clients == null) {
            this.clients = new HashSet<Client>();
        } else {
            this.clients.clear();
        }
        this.clients.addAll(clients);
    }
    
    /**
     * Adds a client to the list of clients
     * @param client The client to add
     * @return Whether the client was added
     */
    public boolean addClient(Client client) {
        if (this.clients == null) {
            this.clients = new HashSet<Client>();
        }
        return this.clients.add(client);
    }
    
    /**
     * Removes a client from the list of clients
     * @param client The client to remove
     * @return Whether the client was removed
     */
    public boolean removeClient(Client client) {
        if (this.clients == null) {
            this.clients = new HashSet<Client>();
        }
        return this.clients.remove(client);
    }
    
    /**
     * Adds a list of clients to the list of clients
     * @param clients The clients to add
     * @return Whether the clients were added
     */
    public boolean addClients(Collection<Client> clients) {
        if (this.clients == null) {
            this.clients = new HashSet<Client>();
        }
        return this.clients.addAll(clients);
    }
    
    /**
     * Removes a list of clients from the list of clients
     * @param clients The clients to remove
     * @return Whether the clients were removed
     */
    public boolean removeClients(Collection<Client> clients) {
        if (this.clients == null) {
            this.clients = new HashSet<Client>();
        }
        return this.clients.removeAll(clients);
    }
       
    /**
     * The list of financial periods of a company
     * @return Unmodifiable list of financial periods 
     */
    public List<FinancialPeriod> getFinancialPeriods() {
        if (this.financialPeriods == null) {
            this.financialPeriods = new HashSet<FinancialPeriod>();
        }
        return Collections.unmodifiableList(new ArrayList(financialPeriods));
    }
    
    /**
     * Sets the list of financial periods
     * @param financialPeriods The new set of financial periods
     */
    public void setFinancialPeriods(Collection<FinancialPeriod> financialPeriods) {
        if (this.financialPeriods == null) {
            this.financialPeriods = new HashSet<FinancialPeriod>();
        } else {
            this.financialPeriods.clear();
        }
        this.financialPeriods.addAll(financialPeriods);
    }
    
    /**
     * Adds a financial period to the company
     * @param financialPeriod The financial period to add
     * @return Whether the financial period was added
     */
    public boolean addFinancialPeriod(FinancialPeriod financialPeriod) {
        if (this.financialPeriods == null) {
            this.financialPeriods = new HashSet<FinancialPeriod>();
        }
        return this.financialPeriods.add(financialPeriod);
    }
    
    /**
     * Removes a financial period from the company
     * @param financialPeriod The financial period to remove
     * @return Whether the financial period was removed
     */
    public boolean removeFinancialPeriod(FinancialPeriod financialPeriod) {
        if (this.financialPeriods == null) {
            this.financialPeriods = new HashSet<FinancialPeriod>();
        }
        return this.financialPeriods.remove(financialPeriod);
    }

    /**
     * Adds a list of financial periods to the company
     * @param financialPeriods The financial periods to add
     * @return Whether the financial periods were added
     */
    public boolean addFinancialPeriods(Collection<FinancialPeriod> financialPeriods) {
        if (this.financialPeriods == null) {
            this.financialPeriods = new HashSet<FinancialPeriod>();
        }
        return this.financialPeriods.addAll(financialPeriods);
    }
    
    /**
     * Removes a list of financials period from the company
     * @param financialPeriods The financial periods to remove
     * @return Whether the financial periods were removed
     */
    public boolean removeFinancialPeriods(Collection<FinancialPeriod> financialPeriods) {
        if (this.financialPeriods == null) {
            this.financialPeriods = new HashSet<FinancialPeriod>();
        }
        return this.financialPeriods.removeAll(financialPeriods);
    }

    /**
     * Whether the company is enabled
     * @param enabled The new value
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    /**
     * Whether the company is enabled
     * @return boolean
     */
    public boolean isEnabled() {
        return this.enabled;
    }
    
    /**
     * Saves the company to the persistent store
     * @throws xperiment.metaphor.model.ModelException If there was an error
     */
    public Company save() throws ModelException {

        /*
         * Save the departments
         */
        for (Department department : getDepartments()) {
            department.attach(getApplicationManager());
            department.save();
        }

        try {
            this.entity = (xperiment.metaphor.model.entity.Entity) getDao().persist(this.entity);

            Company c = (Company) getDao().persist(this);
            this.id = c.getId();

            return this;
        } catch (ServiceException e) {
            throw new SaveException("Unable to save company", e);
        }
    }

    /**
     * Sets the company's name
     * @param name The new value
     * @return Company
     */
    public Company name(String name) {
        this.setName(name);
        return this;
    }

    /**
     * Sets the company's telephone number
     * @param telephoneNo The new value
     * @return Company
     */
    public Company telephone(String telephoneNo) {
        this.getEntity().getContactInformation().setTelephoneNo(telephoneNo);
        return this;
    }

    /**
     * Sets the company's cellphone number
     * @param cellphoneNo The new value
     * @return Company
     */
    public Company cellphone(String cellphoneNo) {
        this.getEntity().getContactInformation().setCellphoneNo(cellphoneNo);
        return this;
    }

    /**
     * Sets the company's facsimile number
     * @param facsimileNo The new value
     * @return Company
     */
    public Company facsimile(String facsimileNo) {
        this.getEntity().getContactInformation().setFacsimileNo(facsimileNo);
        return this;
    }

    /**
     * Sets the company's email
     * @param email The new value
     * @return Company
     */
    public Company email(String email) {
        this.getEntity().getContactInformation().setEmail(email);
        return this;
    }

    /**
     * Sets the company's postal address
     * @param postalAddress The new value
     * @return Company
     */
    public Company postalAddress(Address postalAddress) {
        this.getEntity().getContactInformation().setPostalAddress(postalAddress);
        return this;
    }

    /**
     * Sets the company's physical address
     * @param physicalAddress The new value
     * @return Company
     */
    public Company physicalAddress(Address physicalAddress) {
        this.getEntity().getContactInformation().setPhysicalAddress(physicalAddress);
        return this;
    }

    /**
     * Adds a new department
     * @return Department
     */
    public Department addDepartment() {
        return addDepartment(null);
    }

    /**
     * Adds a new department
     * @param name The name of the department
     * @return Department
     */
    public Department addDepartment(String name) {
        Department dept = new Department(name);
        this.getDepartments().add(dept);
        return dept;
    }
}
